#include "sht20.h"
#include "i2c.h"
#include "stdio.h"

SHT20_Data_t sht20_data;

// 私有函数声明
static float SHT20_Calculate_Temperature(uint16_t raw_temp);
static float SHT20_Calculate_Humidity(uint16_t raw_humidity);
static uint8_t SHT20_CheckCrc8(uint8_t *data, uint8_t num_bytes, uint8_t checksum);

void SHT20_Init(void) {
    // 初始化数据结构
    sht20_data.temperature = 0.0f;
    sht20_data.humidity = 0.0f;
    sht20_data.data_valid = 0;

    // 软复位
    SHT20_Soft_Reset();
    HAL_Delay(20); // 等待复位完成

    // 设置分辨率为最高精度（湿度12位，温度14位）
    SHT20_Set_Resolution(SHT20_RES_RH12_T14);
}

HAL_StatusTypeDef SHT20_Soft_Reset(void) {
    uint8_t cmd = SHT20_CMD_SOFT_RESET;
    HAL_StatusTypeDef status;

    status = HAL_I2C_Master_Transmit(&hi2c2, SHT20_I2C_ADDR << 1, &cmd, 1, SHT20_MEASURE_TIMEOUT);
    if (status != HAL_OK) {
        printf("SHT20: Soft reset failed\r\n");
    }
    return status;
}

//设置分辨率
HAL_StatusTypeDef SHT20_Set_Resolution(uint8_t resolution) {
    uint8_t cmd[2];
    HAL_StatusTypeDef status;

    // 读取当前用户寄存器值
    cmd[0] = SHT20_CMD_READ_USER_REG;
    status = HAL_I2C_Master_Transmit(&hi2c2, SHT20_I2C_ADDR << 1, &cmd[0], 1, SHT20_MEASURE_TIMEOUT);
    if (status != HAL_OK) {
        printf("SHT20: Read user register failed\r\n");
        return status;
    }

    uint8_t user_reg;
    status = HAL_I2C_Master_Receive(&hi2c2, SHT20_I2C_ADDR << 1, &user_reg, 1, SHT20_MEASURE_TIMEOUT);
    if (status != HAL_OK) {
        printf("SHT20: Read user register value failed\r\n");
        return status;
    }

    // 更新分辨率位，7E即0111 1110，81则为1000 0001，即先清空旧值，再将第7位和第0位保留，其他全置0，因为位7和位0全为1表示我们想要的分辨率
    user_reg = (user_reg & 0x7E) | (resolution & 0x81);

    // 写回用户寄存器
    cmd[0] = SHT20_CMD_WRITE_USER_REG;
    cmd[1] = user_reg;
    status = HAL_I2C_Master_Transmit(&hi2c2, SHT20_I2C_ADDR << 1, cmd, 2, SHT20_MEASURE_TIMEOUT);
    if (status != HAL_OK) {
        printf("SHT20: Write user register failed\r\n");
    }
    return status;
}


//设置加热器，加热器作用：当加热器开启时，传感器周围的空气会被轻微加热，相对湿度会相应降低。通过比较加热前后的湿度测量值，可以验证传感器的准确性。
HAL_StatusTypeDef SHT20_Set_Heater(uint8_t enable) {
    uint8_t cmd[2];
    HAL_StatusTypeDef status;

    // 读取当前用户寄存器值
    cmd[0] = SHT20_CMD_READ_USER_REG;
    status = HAL_I2C_Master_Transmit(&hi2c2, SHT20_I2C_ADDR << 1, &cmd[0], 1, SHT20_MEASURE_TIMEOUT);
    if (status != HAL_OK) {
        printf("SHT20: Read user register failed\r\n");
        return status;
    }

    uint8_t user_reg;
    status = HAL_I2C_Master_Receive(&hi2c2, SHT20_I2C_ADDR << 1, &user_reg, 1, SHT20_MEASURE_TIMEOUT);
    if (status != HAL_OK) {
        printf("SHT20: Read user register value failed\r\n");
        return status;
    }

    // 更新加热器位
    if (enable) {
        user_reg |= SHT20_HEATER_ON;
    } else {
        user_reg &= ~SHT20_HEATER_ON;
    }

    // 写回用户寄存器
    cmd[0] = SHT20_CMD_WRITE_USER_REG;
    cmd[1] = user_reg;
    status = HAL_I2C_Master_Transmit(&hi2c2, SHT20_I2C_ADDR << 1, cmd, 2, SHT20_MEASURE_TIMEOUT);
    if (status != HAL_OK) {
        printf("SHT20: Write user register failed\r\n");
    }
    return status;
}

HAL_StatusTypeDef SHT20_Read_Data(void) {
    uint8_t cmd;
    uint8_t data[3];
    HAL_StatusTypeDef status;
    uint16_t raw_temp, raw_humidity;

    // 读取温度
    cmd = SHT20_CMD_TRIG_T_MEASURE_HOLD;
    status = HAL_I2C_Master_Transmit(&hi2c2, SHT20_I2C_ADDR << 1, &cmd, 1, SHT20_MEASURE_TIMEOUT);
    if (status != HAL_OK) {
        printf("SHT20: Temperature measurement command failed\r\n");
        sht20_data.data_valid = 0;
        return status;
    }

    status = HAL_I2C_Master_Receive(&hi2c2, SHT20_I2C_ADDR << 1, data, 3, SHT20_MEASURE_TIMEOUT);
    if (status != HAL_OK) {
        printf("SHT20: Temperature data read failed\r\n");
        sht20_data.data_valid = 0;
        return status;
    }

    // 检查 CRC 校验和
    if (!SHT20_CheckCrc8(data, 2, data[2])) {
        printf("SHT20: Temperature checksum error\r\n");
        sht20_data.data_valid = 0;
        return HAL_ERROR;
    }

    raw_temp = ((uint16_t)data[0] << 8) | data[1];
    sht20_data.temperature = SHT20_Calculate_Temperature(raw_temp);

    // 读取湿度
    cmd = SHT20_CMD_TRIG_RH_MEASURE_HOLD;
    status = HAL_I2C_Master_Transmit(&hi2c2, SHT20_I2C_ADDR << 1, &cmd, 1, SHT20_MEASURE_TIMEOUT);
    if (status != HAL_OK) {
        printf("SHT20: Humidity measurement command failed\r\n");
        sht20_data.data_valid = 0;
        return status;
    }

    status = HAL_I2C_Master_Receive(&hi2c2, SHT20_I2C_ADDR << 1, data, 3, SHT20_MEASURE_TIMEOUT);
    if (status != HAL_OK) {
        printf("SHT20: Humidity data read failed\r\n");
        sht20_data.data_valid = 0;
        return status;
    }

    // 检查 CRC 校验和
    if (!SHT20_CheckCrc8(data, 2, data[2])) {
        printf("SHT20: Humidity checksum error\r\n");
        sht20_data.data_valid = 0;
        return HAL_ERROR;
    }

    raw_humidity = ((uint16_t)data[0] << 8) | data[1];
    sht20_data.humidity = SHT20_Calculate_Humidity(raw_humidity);

    sht20_data.data_valid = 1;
    printf("SHT20: Temperature = %.2f°C, Humidity = %.2f%%\r\n", 
           sht20_data.temperature, sht20_data.humidity);
    return HAL_OK;
}

static float SHT20_Calculate_Temperature(uint16_t raw_temp) {
    // 清除状态位,实际上我们采集的16位数据，分辨率只有14位，因为最后的两位不能算进去，这并不是说明我们需要把采集的16位数据当成14位数据来用，
    //而是我们需要把最后两位给清0，也就是将16位数据&0xFFFC（1111 1111 1111 1100）
    raw_temp &= 0xFFFC;
    // 转换为摄氏度
    return -46.85f + 175.72f * raw_temp / 65536.0f;
}

static float SHT20_Calculate_Humidity(uint16_t raw_humidity) {
    // 清除状态位
    raw_humidity &= 0xFFFC;
    // 转换为相对湿度百分比
    return -6.0f + 125.0f * raw_humidity / 65536.0f;
}

// SHT20 CRC-8 校验函数
static uint8_t SHT20_CheckCrc8(uint8_t *data, uint8_t num_bytes, uint8_t checksum) {
    uint8_t crc = 0x00;
    uint8_t i, j;

    for (j = 0; j < num_bytes; j++) {
        crc ^= data[j];
        for (i = 8; i > 0; i--) {
            if (crc & 0x80) {
                crc = (crc << 1) ^ 0x31; // CRC-8 校验多项式: x^8 + x^5 + x^4 + 1 (0x31)
            } else {
                crc = (crc << 1);
            }
        }
    }
    return (crc == checksum);
} 
